#!/bin/perl -w

# ASSP Installer (AI) Script
# (c) Micheal Espinola Jr 2008
# This code is kept tidy with: perltidy.pl -b -ole=unix -l=400

my $scriptVersion = "1.9";    # version of this script

# This program is free software; you can redistribute it and/or modify it under the terms
# of the Creative Commons BY-NC-SA 3 licensing:
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported (CC BY-NC-SA);
# See http://creativecommons.org/licenses/by-nc-sa/3.0/ for more details.

require 5.008008;

#use diagnostics;
use strict;

#use warnings;
use Archive::Zip qw( :ERROR_CODES );
use Digest::MD5;
use File::Copy;
use File::Glob;
use File::Path;
use LWP::Simple;

# INSTALLATION VARIABLES
#(modify at your discretion)
my $asspVersion       = "1.3.3.8";                # version number of ASSP to install
my $asspRootDir       = "c:/assp";                # root directory for ASSP (always use UNIX-like forward-slashes for Perl)
my $asspCorpusDir     = "corpus";                 # subdirectory for message corpus's
my $asspDataBaseDir   = "databases";              # subdirectory for core ASSP databases
my $asspPbDataBaseDir = "$asspDataBaseDir/pb";    # subdirectory for Penalty Box databases
my $asspFilesDir      = "files";                  # subdirectory for regexp and other list-files
my $asspLogsDir       = "logs";                   # subdirectory for logs
my $asspSourceDir     = "source";                 # subdirectory for stored downloads and version archive

# SOURCEFORGE MIRRORS
#(comment out or reorder for performance at your discretion)
my (@sfDlMirrors) = (
    "easynews",                                   # North America: United States (Phoenix, AZ)
    "superb-east",                                # North America: United States (McLean, VA)
    "superb-west",                                # North America: United States (Seattle, WA)
    "internap",                                   # North America: United States (San Jose, CA)
    "belnet",                                     # Europe: Belgium (Brussels)
    "ovh",                                        # Europe: France (Paris)
    "dfn",                                        # Europe: Germany (Berlin)
    "mesh",                                       # Europe: Germany (Duesseldorf)
    "garr",                                       # Europe: Italy (Bologna)
    "surfnet",                                    # Europe: Netherlands (Amsterdam)
    "switch",                                     # Europe: Switzerland (Lausanne)
    "kent",                                       # Europe: United Kingdom (Kent)
    "ufpr",                                       # South America: Brazil (Curitiba)
    "jaist",                                      # Asia: Japan (Ishikawa)
    "nchc",                                       # Asia: Taiwan (Tainan)
    "optusnet",                                   # Australia: Australia (Sydney)
);

# PERL MODULES USED BY ASSP
#(comment out to prevent installation at your discretion, but do NOT re-order)
my (@asspPerlModules) = (
    "Compress::Zlib",                             # In-memory [de]compression functions (required)
    "Digest::MD5",                                # MD5 algorithm for hashes (required)
    "Time::HiRes",                                # Interval timers for CPU statistics (required)
    "Net::DNS",                                   # DNS lookup functions (required)
    "Net::CIDR::Lite",                            # CIDR addressing functions (optional, but highly recommended)
    "Net::IP::Match::Regexp",                     # Network range addressing functions (optional, but highly recommended)
    "Net::SenderBase",                            # SenderBase.com query functions (optional, but highly recommended)
    "Net::LDAP",                                  # LDAP lookup functions (optional, but highly recommended)
    "Email::Valid",                               # RFC 2822 compliance functions (optional, but highly recommended)
    "Mail::SPF",                                  # SPF lookup functions( optional, but highly recommended)
    "Mail::SPF::Query",                           # SPF lookup functions( optional, but highly recommended)
    "Mail::SRS",                                  # SRS header re-write functions (optional, but highly recommended)
    "File::ReadBackwards",                        # maillog search functions (optional, but highly recommended)
    "File::Scan::ClamAV",                         # Clam AntiVirus functions (optional, but highly recommended)
    "LWP::Simple",                                #
    "Tie::RDBM",                                  #  (optional, but highly recommended)
);
if ( $^O eq "MSWin32" ) {                         # WINDOWS ONLY MODULES
    push( @asspPerlModules, "Net::Syslog" );      # Syslog logging functions (optional, for Windows platforms only)
    push( @asspPerlModules, "Win32::Daemon" );    # Windows service functions (optional, for Windows platforms only)
}
else {                                            # UNIX-LIKE ONLY MODULES
    push( @asspPerlModules, "Sys::Syslog" );      # Syslog logging functions (optional, for Unix-like platforms only)
}
if ( $asspVersion ge "1" ) {                      # ASSP MULTI-THREAD MODULES
    push( @asspPerlModules, "Convert::TNEF" );            #
    push( @asspPerlModules, "Mail::DKIM::Verifier" );     #
    push( @asspPerlModules, "Email::MIME" );              #
    push( @asspPerlModules, "Email::MIME::Modifier" );    #
    push( @asspPerlModules, "Email::Send" );              #
    push( @asspPerlModules, "Email::Valid" );             #
    push( @asspPerlModules, "File::Which" );              #
    push( @asspPerlModules, "IO::Poll" );                 #
    push( @asspPerlModules, "MIME::Types" );              #
    push( @asspPerlModules, "Net::Domain::TLD" );         #
    push( @asspPerlModules, "Net::SMTP" );                #
    push( @asspPerlModules, "NetAddr::IP" );              #
    push( @asspPerlModules, "PDF::OCR" );                 # ???
    push( @asspPerlModules, "threads" );                  #
    push( @asspPerlModules, "threads::shared" );          #
    push( @asspPerlModules, "Thread::Queue" );            #
    push( @asspPerlModules, "Thread::state" );            #
}

##### NOTHING TO CASUALLY MODIFY BEYOND THIS POINT #####

# GRAB COMMAND LINE VARIABLES IF SPECIFIED
for (@ARGV) {
    if ( $_ =~ /^\d+\.\d+\.\d+(\.\d+)?$/ ) {              # grab ASSP version info
        $asspVersion = $_;
    }
    if ( $_ =~ /\\/ ) {                                   # make any path symbols to be UNIX-like forward-slashes
        $_ =~ s/\\/\//;
    }
    if ( $_ =~ /\// ) {                                   # grab installation directory
        $asspRootDir = $_;
    }
}

# VARIABLE CONCATENATIONS
#(do not modify - variables are used throughout the script)
my $asspRelease = "ASSP_$asspVersion-Install";            # name of ASSP distribution
my $asspZipFile = "$asspRelease.zip";                     # name of ASSP distribution Zip file
my $asspMD5File = "$asspRelease.md5";                     # name of ASSP distribution MD5 checksum file
my $asspSSDFile = "sampleSpamCorpus.zip";                 # name of ASSP sample spam corpus file
my $asspArchive = "$asspRootDir/$asspSourceDir";          # location for source/archived files
my $asspZipPath = "$asspArchive/$asspZipFile";            # location for ASSP distribution Zip file
my $asspMD5Path = "$asspArchive/$asspMD5File";            # location for ASSP distribution MD5 checksum file
my $asspRootCfg = "$asspRootDir/assp.cfg";                # location for ASSP configuration file

# ASSP DIRECTORY NAMES - NOT MODIFIABLE
#(do not modify, currently hard-coded in assp.pl)
my $asspDocsDir    = "docs";                              # subdirectory for documentation
my $asspImagesDir  = "images";                            # subdirectory for images and CSS for web interface
my $asspNotesDir   = "notes";                             # subdirectory for notes
my $asspReportsDir = "reports";                           # subdirectory for reports

# ASSP DIRECTORY STRUCTURE
#(do not modify, dynamically built from other variables)
my (@modifySubDirs) = (
    "$asspCorpusDir",                                     # subdirectory for corpus directory tree
    "$asspCorpusDir/errors",                              # subdirectory for errors submittal tree
    "$asspCorpusDir/errors/notspam",                      # subdirectory for ham false-positive mail
    "$asspCorpusDir/errors/spam",                         # subdirectory for spam false-negative mail
    "$asspCorpusDir/notspam",                             # subdirectory for ham (good) mail
    "$asspCorpusDir/okmail",                              # subdirectory for ham, but not for inclusion in Bayesian DB
    "$asspCorpusDir/spam",                                # subdirectory for spam (bad) mail
    "$asspCorpusDir/viruses",                             # subdirectory for virus (rejected attachments) mail
    "$asspDataBaseDir",                                   # subdirectory for various databases
    "$asspPbDataBaseDir",                                 # subdirectory for penalty box databases
    "$asspDocsDir",                                       # subdirectory for documentation
    "$asspFilesDir",                                      # subdirectory for regexp and other list-files
    "$asspImagesDir",                                     # subdirectory for images and CSS for web interface
    "$asspLogsDir",                                       # subdirectory for maillog
    "$asspNotesDir",                                      # subdirectory for notes
    "$asspReportsDir",                                    # subdirectory for reports
    "$asspSourceDir",                                     # subdirectory for stored downloads and version archive
);

# PERL REPOSITORIES FOR MODULES USED BY ASSP
my (@perlRepositories) = (
    "\"Roth Consulting\" http://www.roth.net/perl/packages/",           # Roth Consulting, makers of Win32::Daemon ()
    "\"Serguei Trouchelle\" http://trouchelle.com/ppm/",                # Serguei Trouchelle, Win32 repository (http://trouchelle.com/perl/ppmrepview.pl)
    "\"University of Winnipeg\" http://theoryx5.uwinnipeg.ca/ppms/",    # University of Winnipeg repository
                                                                        #    "\"BioPerl\" http://bioperl.org/DIST/",                                     #
                                                                        #    "\"CrazyInsomniac\" http://crazyinsomniac.perlmonk.org/perl/ppm/5.8/",      #
                                                                        #    "\"Jenda Krynicky\" http://Jenda.Krynicky.cz/perl/",                        #
                                                                        #    "\"OpenInteract\" http://openinteract.sourceforge.net/ppmpackages/5.8/",    #
                                                                        #    "\"Softmatic DBI\" ftp://ftp.esoftmatic.com/outgoing/DBI/",                 #
                                                                        #    "\"Soulcage\" http://www.soulcage.net/ppds.58/",                            #
                                                                        #    "\"Steffen Mueller\" http://steffen-mueller.net/modules/repository/",       #
);

print "\n";
print "ASSP Artificial Intelligence script v$scriptVersion\n";
print "\n";

# COMMAND-LINE ACTIONS AND SCRIPT LOGIC
my ( $scriptAction, $scriptVerb, $MD5Result, $xConfirm, $scriptStartTime, $scriptEndTime, $dlStartTime, $dlEndTime );
my ( $zipError, $copyError );

my $badSwitch = 0;
if (@ARGV) {
    $scriptStartTime = time();
    if ( ( lc( $ARGV[0] ) eq "-i" ) || ( lc( $ARGV[0] ) eq "-il" ) ) {
        $scriptAction = "install";
        $scriptVerb   = "Installing/Updating";
        &modifySubDirs;
        if ( lc( $ARGV[0] ) ne "-il" ) {
            $dlStartTime = time();
            &modifyPerlRepositories;
            &modifyPerlModules;
            &downloadFiles;
            $dlEndTime = time();
        }
        &CheckMD5( $asspMD5File, $asspMD5Path, $asspZipFile, $asspZipPath );
        if ( $MD5Result == 0 ) {
            &unzipASSP;
            &prepASSPconfig;
            &modifyService;
        }
        else { print "Extraction aborted\n\n"; }
    }
    elsif ( ( lc( $ARGV[0] ) eq "-u" ) || ( lc( $ARGV[0] ) eq "-ul" ) ) {
        $scriptAction = "update";
        $scriptVerb   = "Updating";
        if ( lc( $ARGV[0] ) ne "-ul" ) {
            $dlStartTime = time();
            &downloadFiles;
            $dlEndTime = time();
        }
        &CheckMD5( $asspMD5File, $asspMD5Path, $asspZipFile, $asspZipPath );
        if ( $MD5Result == 0 ) {
            &unzipASSP;
        }
        else { print "Extraction aborted\n\n"; }
    }
    elsif ( lc( $ARGV[0] ) eq "-x" ) {
        print "You are about to complete remove the ASSP installation located in:\n";
        print "\n  $asspRootDir\n";
        print "\nAre you sure (y/n)? ";
        $xConfirm = <STDIN>;
        chomp($xConfirm);
        if ( lc($xConfirm) eq "y" ) {
            print "Are you really sure (y/n)? ";
            $xConfirm = <STDIN>;
            chomp($xConfirm);
            if ( lc($xConfirm) eq "y" ) {
                print "\n";
                $scriptAction = "remove";
                $scriptVerb   = "Removing";
                &modifyService;
                &modifyPerlModules;
                &modifySubDirs;
            }
            else {
                print "\nInvalid choice\n" if ( lc($xConfirm) ne "n" );
                $xConfirm = "n";
            }
        }
        else {
            print "\nInvalid choice\n" if ( lc($xConfirm) ne "n" );
            $xConfirm = "n";
        }
    }
    else {
        $badSwitch = 1;
    }
    $scriptEndTime = time();
}
else {
    $badSwitch = 1;
}
if ( $badSwitch == 1 ) { &displayDirections; }
elsif ( lc($xConfirm) ne "n" ) { &displayRunTime; }

# ADD ADDITIONAL REPOSITORIES IF NECESSARY
sub modifyPerlRepositories {
    my ( $perlRepository, $perlRepositoryTitle, $asspRepositoryCount, $asspRepositoriesCount );
    $asspRepositoriesCount = 0;
    print ";----------------------------------------;\n";
    print ";  $scriptVerb Perl Repositories\n";
    print ";----------------------------------------;\n\n";
    print "Working (stdout):\n----------\n";
    foreach $perlRepository (@perlRepositories) {
        $asspRepositoryCount = 0;
        if ( $perlRepository =~ /\"(.*?)\"/ ) {
            $perlRepositoryTitle = $1;
        }
        system("ppm repo list>$asspRootDir/$asspSourceDir/stdoutput.txt 2>&1");
        open( STDOUTPUT, "$asspRootDir/$asspSourceDir/stdoutput.txt" ) or print "Can\'t open \"$asspRootDir/$asspSourceDir/stdoutput.txt\": $!\n";
        while ( $_ = <STDOUTPUT> ) {
            if ( $_ =~ /\b$perlRepositoryTitle\b/ ) {
                $asspRepositoryCount++;
                $asspRepositoriesCount++;
            }
        }
        if ( $asspRepositoryCount == 0 ) {
            print "Adding $perlRepository\n";
            &repositoryWork($perlRepository);
        }
    }
    if ( $asspRepositoriesCount >= 1 ) {
        print "No missing repositories to install. Syncing local repository caches:\n";
        system("ppm repo sync");
    }
    print "----------\n\n";
}

# INSTALL PERL MODULES IF NOT AVAILABLE
sub modifyPerlModules {
    print ";----------------------------------------;\n";
    print ";  $scriptVerb Perl Modules\n";
    print ";----------------------------------------;\n\n";
    print "Working (stdout):\n----------\n";
    my ( $perlModule, $asspModuleTestCount );
    $asspModuleTestCount = 0;
    if ( $scriptAction eq "install" ) {
        foreach $perlModule (@asspPerlModules) {
            eval "use $perlModule";
            if ($@) {
                &moduleWork( $scriptAction, $perlModule );
                $asspModuleTestCount++;
            }
        }
        print "No missing modules to install\n" if ( $asspModuleTestCount == 0 );
    }
    if ( $scriptAction eq "remove" ) {
        @asspPerlModules = reverse(@asspPerlModules);
        foreach $perlModule (@asspPerlModules) {
            eval "use $perlModule";
            if ($@) { }
            else {
                if ( $perlModule eq "Compress::Zlib" || $perlModule eq "Time::HiRes" ) { }
                else {
                    &moduleWork( $scriptAction, $perlModule );
                }
                $asspModuleTestCount++;
            }
        }
        print "No modules to remove\n" if ( $asspModuleTestCount <= 2 );
    }
    print "----------\n\n";
}

# INSTALL (OR REMOVE) MODULES
sub moduleWork {
    if ( $_[1] eq "Tie::RDBM" )            { $_[1] = "Tie-DBI"; }
    if ( $_[1] eq "Mail::DKIM::Verifier" ) { $_[1] = "Mail-DKIM"; }
    if ( $_[1] eq "Net::LDAP" )            { $_[1] = "perl-ldap"; }
    if ( $_[1] eq "Win32::Daemon" )        { $_[1] = "Win32-Daemon"; }
    if ( $^O   eq "MSWin32" ) {
        system("ppm $_[0] --area site --force $_[1]");    # Windows module installation via PPM
    }
    else {
        system("perl -MCPAN -e $_[0] $_[1]");             # *NIX module installation via CPAN
    }
}

# INSTALL REPOSITORIES
sub repositoryWork {
    if ( $^O eq "MSWin32" ) {
        system("ppm repo add $_[0]");
    }
    else {
        print "Not implimented for *nix - yet\n";
    }
}

# MODIFY DIRECTORY STRUCTURE
sub modifySubDirs {
    print ";----------------------------------------;\n";
    print ";  $scriptVerb Directory Structure\n";
    print ";----------------------------------------;\n\n";
    print "Working (stdout):\n----------\n";
    my ($asspSubDir);
    my $asspSubDirTestCount = 0;
    if ( $scriptAction eq "install" ) {
        foreach $asspSubDir (@modifySubDirs) {
            if ( -e "$asspRootDir/$asspSubDir" && -d "$asspRootDir/$asspSubDir" ) { }
            else {
                mkpath( "$asspRootDir/$asspSubDir", 1, 01777 );
                $asspSubDirTestCount++;
            }
        }
        if ( $asspSubDirTestCount == 0 ) {
            print "No missing directories to create\n";
        }
        else { }
    }
    if ( $scriptAction eq "remove" ) {
        if ( -e "$asspRootDir" && -d "$asspRootDir" ) {
            rmtree( $asspRootDir, 1 );
            $asspSubDirTestCount++;
        }
        if ( $asspSubDirTestCount == 0 ) {
            print "No directories to delete\n";
        }
    }
    print "----------\n\n";
}

# ASSP DECOMPRESSION FROM ZIP
sub unzipASSP {
    print ";----------------------------------------;\n";
    print ";  Decompressing ASSP Distribution File\n";
    print ";----------------------------------------;\n\n";
    print "Working (stdout):\n----------\n";
    chdir $asspRootDir;
    my $zipError  = 0;
    my $copyError = 0;
    if ( -e "$asspZipPath" ) {
        my ( $asspSubDir, $zipFileListing );
        my $zipFile = Archive::Zip->new();
        unless ( $zipFile->read($asspZipPath) == AZ_OK ) {
            print "Unable to open $zipFile\n";
        }
        else {
            unless ( $zipFile->extractTree( "", "$asspArchive/" ) == AZ_OK ) {
                print "Unable to extract distribution from $zipFile to $asspArchive\n";
                $zipError = 1;
            }
            else {
                print "Distribution archived to : $asspArchive/$asspRelease\n" if ( $zipError != 1 );
                if ( -d "$asspArchive/$asspRelease/ASSP" ) {
                    print "Distribution copied in to: $asspRootDir\n";
                    my ( $archiveText, $archiveScript, $archiveDoc, $archiveFile, $archiveImage, $archiveReport );
                    my @archiveMain    = glob("$asspArchive/$asspRelease/*.txt");
                    my @archiveRoot    = glob("$asspArchive/$asspRelease/ASSP/*.pl");
                    my @archiveDocs    = glob("$asspArchive/$asspRelease/ASSP/docs/*");
                    my @archiveFiles   = glob("$asspArchive/$asspRelease/ASSP/files/*");
                    my @archiveImages  = glob("$asspArchive/$asspRelease/ASSP/images/*");
                    my @archiveReports = glob("$asspArchive/$asspRelease/ASSP/reports/*");
                    foreach $archiveText   (@archiveMain)   { &copyFiles( "$archiveText",   "$asspDocsDir" ); }
                    foreach $archiveScript (@archiveRoot)   { &copyFiles( "$archiveScript", "$asspRootDir" ); }
                    foreach $archiveDoc    (@archiveDocs)   { &copyFiles( "$archiveDoc",    "$asspDocsDir" ); }
                    foreach $archiveImage  (@archiveImages) { &copyFiles( "$archiveImage",  "$asspImagesDir" ); }

                    foreach $archiveFile (@archiveFiles) {
                        if ( -e "$asspFilesDir/$archiveFile" ) { }
                        else {
                            &copyFiles( "$archiveFile", "$asspFilesDir" );
                        }
                    }
                    foreach $archiveReport (@archiveReports) {
                        if ( -e "$asspReportsDir/$archiveReport" ) { }
                        else {
                            &copyFiles( "$archiveReport", "$asspReportsDir" );
                        }
                    }
                }
            }
        }
    }
    else {
        print "$asspZipPath does not exist. No files to extract\n";
    }
    print "----------\n\n";
    print " [*] New or updated files may exist in:\n     $asspArchive/$asspRelease/ASSP/files\n\n" if ( $copyError == 0 );
}

# ASSP DISTRIBUTION DOWNLOAD SUBROUTINE
sub downloadFiles {
    print ";----------------------------------------;\n";
    print ";  Downloading Files\n";
    print ";----------------------------------------;\n\n";
    print "Working (stdout):\n----------\n";
    if ( -e "$asspRootDir/$asspSourceDir" && -d "$asspRootDir/$asspSourceDir" ) {
        &downloadSourceForge( "assp", $asspZipFile, $asspZipPath );
        &downloadSourceForge( "assp", $asspMD5File, $asspMD5Path );
    }
    else {
        print "$asspRootDir/$asspSourceDir does not exist. Unable to save files\n";
    }
    print "----------\n\n";
}

# FILE COPY SUBROUTINE
sub copyFiles {
    copy( "$_[0]", "$_[1]" ) or $copyError = 1 && print "$_[0] copy failed: $!\n";
}

# SOURCEFORGE MIRROR DOWNLOAD SUBROUTINE
sub downloadSourceForge {
    my ($mirrorUrl);
    my $sfDownloadOK   = 0;
    my $sfMirrorNumber = 0;
    my $sfMirrorTotal  = @sfDlMirrors;
    until ( $sfDownloadOK == 1 || $sfMirrorNumber == $sfMirrorTotal ) {
        $mirrorUrl = "http://" . $sfDlMirrors[$sfMirrorNumber] . ".dl.sourceforge.net/sourceforge/$_[0]/" . $_[1];
        print $_[1] . " from \"" . $sfDlMirrors[$sfMirrorNumber] . "\" SF mirror ";
        $sfDownloadOK = &download( $mirrorUrl, $_[2] );
        unless ($sfDownloadOK) { print "failed, trying next...\n"; }
        $sfMirrorNumber++;
    }
    if ( $sfDownloadOK == 1 ) {
        print "OK\n";
    }
    else {
        print "\n [*] Unable to locate " . $_[1] . " on $sfMirrorTotal known mirrors\n\n";
    }
}

# DOWNLOAD SUBROUTINE
sub download {
    is_success( getstore( $_[0], $_[1] ) );
}

# MD5 HASH COMPARISON
sub CheckMD5 {
    print ";----------------------------------------;\n";
    print ";  Validating Distribution\n";
    print ";----------------------------------------;\n\n";
    if ( -e "$_[1]" && -e "$_[3]" ) {
        print "Comparing MD5 hash:\n----------\n";
        my ( $fileMD5Hash, $textMD5Hash );
        open( MD5FILE, "$_[1]" ) or print "Can\'t open \"$_[1]\": $!";
        while ( $_ = <MD5FILE> ) {
            if ( $_ =~ /\b\w{32}\b/ ) {
                $fileMD5Hash = substr( $_, 0, 32 );
            }
        }
        open( ZIPFILE, "$_[3]" ) or print "Can\'t open \"$_[3]\": $!";
        binmode(ZIPFILE);
        $textMD5Hash = Digest::MD5->new->addfile(*ZIPFILE)->hexdigest;
        print "$_[0] : $fileMD5Hash\n$_[2] = $textMD5Hash\n";
        print "----------\n\n";
        if ( lc($textMD5Hash) eq lc($fileMD5Hash) ) {
            $MD5Result = 0;
            print " [*] MD5 matches - download is valid\n\n";
        }
        else {
            print "MD5 does not match - download is not invalid\n\n";
            $MD5Result = 1;
        }
        return ($MD5Result);
    }
    else {
        print "$_[0] and/or $_[2] does not exist\nCannot validate file\n\n";
        $MD5Result = 1;
    }
}

# WINDOWS SERVICE INSTALLER
sub modifyService {
    print ";----------------------------------------;\n";
    print ";  Modifying Service/Daemon\n";
    print ";----------------------------------------;\n\n";
    print "Working (stdout):\n----------\n";
    if ( $^O eq "MSWin32" ) {
        if ( $scriptAction eq "install" ) {
            if ( -e "$asspRootDir/addservice.pl" ) {
                system("perl S:/Scripts/assp-ai/svn/addservice.pl -i $asspRootDir/assp.pl $asspRootDir");
                system("perl S:/Scripts/assp-ai/svn/addservice.pl -s");
#                system("perl $asspRootDir/addservice.pl -i $asspRootDir/assp.pl $asspRootDir");
#                system("perl $asspRootDir/addservice.pl -s");
            }
        }
        if ( $scriptAction eq "remove" ) {
            if ( -e "$asspRootDir/addservice.pl" ) {
                system("perl $asspRootDir/addservice.pl -u ");
            }
        }
    }
    else {
        print "Not currently applicable.\n";
    }
    print "----------\n\n";
}

# INITIAL CONFIGURATION FILE TWEAKS
sub prepASSPconfig {
    if ( -e "$asspRootCfg" ) { }
    else {
        print ";----------------------------------------;\n";
        print ";  Preparing ASSP Configuration File\n";
        print ";----------------------------------------;\n\n";
        open( CFGFILE, ">>$asspRootCfg" );
        if ( $^O eq "MSWin32" ) {
            print CFGFILE "AsAService:=1\n";
        }
        else {
            print CFGFILE "AsADaemon:=1\n";
        }

        # Server Response Codes And Verbose Messages
        print CFGFILE "AttachmentError:=550 5.7.7 Your message has been rejected because it contains a file attachment which we do not allow for security purposes. Please compress any attachments before mailing, or contact your local e-mail support for assistance. You may need to be added to our whitelist before you are allowed to send this attachment. This attempt has been logged\n";
        print CFGFILE "AvError:=550 5.7.7 Your message has been rejected because it appears to be infected with the virus/Trojan: '\$infection'. Clean and re-send the message, or contact your local e-mail support for assistance. This attempt has been logged\n";
        print CFGFILE "DelayError:=451 4.7.1 Your message has been intentionally delayed for anti-spam validation purposes. It will be accepted after a 5-minute waiting period. Re-send your message then, or wait for your server to retry on its own. This attempt has been logged\n";
        print CFGFILE "NoRelaying:=550 5.7.1 Your message has been rejected because forwarding/relaying to remote hosts is not allowed. This attempt has been logged\n";
        print CFGFILE "NoValidRecipient:=550 5.1.1 Your message has been rejected because the intended recipient <EMAILADDRESS> is unknown or no longer valid. This attempt has been logged\n";
        print CFGFILE "PenaltyError:=550 5.7.7 Your message has been rejected because your IP address has been blocked for 1 hour due to repeated spam or SMTP standards violations. Contact your local e-mail support for assistance. This attempt has been logged\n";
        print CFGFILE "RBLError:=550 5.7.1 Your message has been rejected because your domain is currently blacklisted by the RBLLISTED DNSBL. Contact your local e-mail support for assistance. This attempt has been logged\n";
        print CFGFILE "SPFError:=550 5.7.1 Your message has been rejected because it failed Sender Policy Framework (SPF) validation Contact your local e-mail support for assistance. This attempt has been logged: SPFRESULT\n";
        print CFGFILE "SPFSoftError:=550 5.7.1 Your message has been rejected because it failed Sender Policy Framework (SPF) validation. Contact your local e-mail support for assistance. This attempt has been logged: SPFRESULT\n";
        print CFGFILE "SenderInvalidError:=554 5.7.7 Your message has been rejected because it failed REASON policy violations. Contact your local e-mail support for assistance. This attempt has been logged\n";
        print CFGFILE "SpamError:=550 5.7.7 Your message has been rejected because unsolicited commercial email (UCE) is not allowed by domain policy. Contact your local e-mail support for assistance. This attempt has been logged\n";
        print CFGFILE "URIBLError:=550 5.7.1 Your message has been rejected because it contained links blacklisted by the URIBLNAME URIBL. Contact your local e-mail support for assistance. This attempt has been logged\n";
        print CFGFILE "URIBLPolicyError:=550 5.7.1 Your message has been rejected because it failed our URIBL domain policy. Contact your local e-mail support for assistance. This attempt has been logged\n";
        print CFGFILE "UuencodedError:=550 5.7.7 Your message has been rejected because it is encoded by UUENCODE. Please use MIME instead, or contact your local e-mail support for assistance. This attempt has been logged\n";
        print CFGFILE "bombError:=550 5.7.7 Your message has been rejected because it contains a phrase we do not allow and you are an unknown sender. Reword your message and send again, or contact your local e-mail support for assistance. This attempt has been logged\n";
        print CFGFILE "scriptError:=550 5.7.7 Your message has been rejected because it contains HTML scripting code. Re-send with offending code removed, or contact your local e-mail support for assistance. This attempt has been logged\n";

        # List File Locations
        print CFGFILE "BounceSenders:=file:$asspFilesDir/bounceSenders.txt\n";
        print CFGFILE "RBLServiceProvider:=file:$asspFilesDir/RBLServiceProvider.txt\n";
        print CFGFILE "RWLServiceProvider:=file:$asspFilesDir/RWLServiceProvider.txt\n";
        print CFGFILE "URIBLCCTLDS:=file:$asspFilesDir/URIBLCCTLDS.txt\n";
        print CFGFILE "URIBLServiceProvider:=file:$asspFilesDir/URIBLServiceProvider.txt\n";
        print CFGFILE "allowAdminConnectionsFrom:=file:$asspFilesDir/allowAdminConnectionsFrom.txt\n";
        print CFGFILE "baysSpamLovers:=file:$asspFilesDir/mixedSpamLovers.txt\n";
        print CFGFILE "blackListedDomains:=file:$asspFilesDir/blackListedDomains.txt\n";
        print CFGFILE "bombDataRe:=file:$asspFilesDir/bombDataRe.txt\n";
        print CFGFILE "bombHeaderRe:=file:$asspFilesDir/bombHeaderRe.txt\n";
        print CFGFILE "bombRe:=file:$asspFilesDir/bombRe.txt\n";
        print CFGFILE "bombSenderRe:=file:$asspFilesDir/bombSenderRe.txt\n";
        print CFGFILE "denySMTPConnectionsFrom:=file:$asspFilesDir/denySMTPConnectionsFrom.txt\n";
        print CFGFILE "exportExtremeBlack:=file:$asspFilesDir/exportExtremeBlack.txt\n";
        print CFGFILE "heloBlacklistIgnore:=file:$asspFilesDir/heloBlacklistIgnore.txt\n";
        print CFGFILE "invalidFormatHeloRe:=file:$asspFilesDir/invalidFormatHeloRe.txt\n";
        print CFGFILE "ispip:=file:$asspFilesDir/ispip.txt\n";
        print CFGFILE "localDomains:=file:$asspFilesDir/localDomains.txt\n";
        print CFGFILE "myServerRe:=file:$asspFilesDir/myServerRe.txt\n";
        print CFGFILE "noDelay:=file:$asspFilesDir/noDelay.txt\n";
        print CFGFILE "noPB:=file:$asspFilesDir/noPB.txt\n";
        print CFGFILE "noProcessing:=file:$asspFilesDir/noProcessing.txt\n";
        print CFGFILE "noProcessingDomains:=file:$asspFilesDir/noProcessingDomains.txt\n";
        print CFGFILE "noProcessingIPs:=file:$asspFilesDir/noProcessingIPs.txt\n";
        print CFGFILE "noScan:=file:$asspFilesDir/noScan.txt\n";
        print CFGFILE "npRe:=file:$asspFilesDir/npRe.txt\n";
        print CFGFILE "pbSpamLovers:=file:$asspFilesDir/mixedSpamLovers.txt\n";
        print CFGFILE "redRe:=file:$asspFilesDir/redRe.txt\n";
        print CFGFILE "scriptRe:=file:$asspFilesDir/scriptRe.txt\n";
        print CFGFILE "spamtrapaddresses:=file:$asspFilesDir/spamtrapaddresses.txt\n";
        print CFGFILE "spfSpamLovers:=file:$asspFilesDir/mixedSpamLovers.txt\n";
        print CFGFILE "testRe:=file:$asspFilesDir/testRe.txt\n";
        print CFGFILE "validFormatHeloRe:=file:$asspFilesDir/validFormatHeloRe.txt\n";
        print CFGFILE "whiteListedDomains:=file:$asspFilesDir/whiteListedDomains.txt\n";
        print CFGFILE "whiteRe:=file:$asspFilesDir/whiteRe.txt\n";

        # Corpus Locations
        print CFGFILE "correctednotspam:=$asspCorpusDir/errors/notspam\n";
        print CFGFILE "correctedspam:=$asspCorpusDir/errors/spam\n";
        print CFGFILE "incomingOkMail:=$asspCorpusDir/okmail\n";
        print CFGFILE "notspamlog:=$asspCorpusDir/notspam\n";
        print CFGFILE "spamlog:=$asspCorpusDir/spam\n";
        print CFGFILE "viruslog:=$asspCorpusDir/viruses\n";

        # Database File Locations
        print CFGFILE "DelayShowDB:=file:$asspDataBaseDir/delay.white.db\n";
        print CFGFILE "DelayShowDBwhite:=file:$asspDataBaseDir/delay.db\n";
        print CFGFILE "delaydb:=$asspDataBaseDir/delay.db\n";
        print CFGFILE "greylist:=$asspDataBaseDir/greylist.db\n";
        print CFGFILE "griplist:=$asspDataBaseDir/griplist.db\n";
        print CFGFILE "pbdb:=$asspPbDataBaseDir/pb\n";
        print CFGFILE "redlistdb:=$asspDataBaseDir/redlist.db\n";
        print CFGFILE "spamdb:=$asspDataBaseDir/spam.db\n";
        print CFGFILE "whitelistdb:=$asspDataBaseDir/whitelist.db\n";

        # Log File Locations
        print CFGFILE "logfile:=$asspLogsDir/maillog.txt\n";

        # Misc Settings
        print CFGFILE "maillogExt:=.eml\n";
        print CFGFILE "spamSubject:=[spam] \n";

        # EOF
        close(CFGFILE);
        print "Default $asspRootCfg created\n\n";
    }
}

# ERRORS (NOT IMPLEMENTED)
sub errors {
    print ";----------------------------------------;\n";
    print ";  Script Errors\n";
    print ";----------------------------------------;\n\n";
}

# USAGE INSTRUCTIONS
#        [===============================[80 column spacer]================================]
sub displayDirections {
    print "Syntax:\n\n";
    print "  perl $0 <option> [version] [directory]\n\n";
    print "Options:\n\n";
    print "  -i  : Install: Installation will download the distribution from SourceForge,\n";
    print "        create directory structures, a baseline assp.cfg file, and install\n";
    print "        required Perl modules\n";
    print "  -il : Same as -i, but installs ASSP code from a locally cached copy\n";
    print "  -u  : Update: Update will only overwrite ASSP distribution files. It will\n";
    print "        download a new copy of the distribution.\n";
    print "  -il : Same as -u, but updates ASSP code from a locally cached copy\n";
    print "  -x  : Remove the installation of ASSP\n\n";
    print "Example usage for Windows:\n\n";
    print "  perl $0 -i $asspVersion c:\\assp\n";
    print "  perl $0 -ul $asspVersion c:\\assp\n";
    print "  perl $0 -x c:\\assp\n\n";
    print "Example usage for Unix-like platforms:\n\n";
    print "  perl $0 -i $asspVersion /opt/assp\n";
    print "  perl $0 -ul $asspVersion /opt/assp\n";
    print "  perl $0 -x /opt/assp\n";
}

sub displayRunTime {
    print ";----------------------------------------;\n";
    print ";  Script Run Time\n";
    print ";----------------------------------------;\n\n";
    if ( $dlEndTime && $dlStartTime ) {
        print "Download time  : ", ( $dlEndTime - $dlStartTime ), " seconds\n";
    }
    if ( $scriptEndTime && $scriptStartTime ) {
        print "Execution time : ", ( $scriptEndTime - $scriptStartTime ), " seconds\n";
    }
}

__END__

